package crack;

import inf.jlinalg.IVector;
import fem2.Debugger;
import fem2.MeshUtilities;
import fem2.Model;
import fem2.Node;
import fem2.analysis.Analysis;
import fem2.enu.EchoLevelType;
import fem2.observer.StressObserver;
import fem2.observer.TimeStepObserver;
import fem2.observer.XYGraphicalObserver;
import fem2.observer.XYSourceObserver;
import fem2.strategies.ArcLengthSolutionStrategyWithDistanceConstraint;
import fem2.strategies.Strategy;

public abstract class AbstractFourPointTest2UsingSDAElementDemo extends
		AbstractFourPointTestUsingSDAElementDemo {

	/**
	 * arc-length solution strategy to control crack mouth sliding
	 * 
	 * @author hbui
	 * 
	 */
	class ArcLengthControlCrackMouthSliding extends ArcLengthSolutionStrategyWithDistanceConstraint {

		public ArcLengthControlCrackMouthSliding(Model m, double[] dArray, Node n1, Node n2) {
			super(m, dArray, n1, n2);
		}

		@Override
		protected double computeConstraintValue(IVector u, IVector u_old, double lambda,
				double lambda_old) {
			int[] dofs1 = n1.getDofNumbers();
			int[] dofs2 = n2.getDofNumbers();
			double dy2 = 0.0;
			double dy2_old = 0.0;
			dy2 = Math.abs(u.get(dofs2[1]) - u.get(dofs1[1]));
			dy2_old = Math.abs(u_old.get(dofs2[1]) - u_old.get(dofs1[1]));
			return dy2 - dy2_old - this.d;
		}

		@Override
		protected void computeGradientOfConstraintWrtU(IVector u, IVector u_old, double lambda,
				double lambda_old, IVector dfdu) {

			int[] dofs1 = n1.getDofNumbers();
			int[] dofs2 = n2.getDofNumbers();

			for (int i = 0; i < dfdu.getSize(); i++) {
				dfdu.set(i, 0.0);
			}

			dfdu.set(dofs1[1], Math.signum(u.get(dofs1[1]) - u.get(dofs2[1])));
			dfdu.set(dofs2[1], Math.signum(u.get(dofs2[1]) - u.get(dofs1[1])));
		}
	}

	public AbstractFourPointTest2UsingSDAElementDemo() {
		projectDir = "/home/hbui/kratos_janosch";
		projectName = "four-point-test2.gid";
	}

	protected Strategy createStrategy(Model m) {
		throw new Error(
				"this methos is not supported. Please use createStrategy(Model m, double[] dArray) instead");
	}

	/**
	 * create the strategy
	 * 
	 * @param m
	 * @param dArray
	 * @return
	 */
	protected Strategy createStrategy(Model m, double[] dArray) {
		Node n1 = MeshUtilities.seekNodeNearest(m.getMesh(), 654.0, 0);
		Node n2 = MeshUtilities.seekNodeNearest(m.getMesh(), 668.0, 0);
		return new ArcLengthControlCrackMouthSliding(m, dArray, n1, n2);
	}

	@Override
	public void addObservers(final Model m, final Strategy s, TimeStepObserver o) {
		addFundamentalObservers(m, s, o);
		addObserversForCrackProblem(m, s, o);

		s.addObserver(new StressObserver(m));

		XYSourceObserver xyo = new XYSourceObserver() {

			Node n1 = MeshUtilities.seekNodeNearest(m.getMesh(), 654.0, 0);
			Node n2 = MeshUtilities.seekNodeNearest(m.getMesh(), 668.0, 0);

			@Override
			public void fireTimeStepFinished() {
				double[] u1 = n1.getUHatLocal();
				double[] u2 = n2.getUHatLocal();
				double du = Math.abs(u1[1] - u2[1]);
				double lambda = s.getCurrentTimeStep();
				setData(du, lambda);
			}

		};

		s.addObserver(xyo);
		s.addObserver(new XYGraphicalObserver(xyo, "lambda-mouth sliding plot"));
	}

	@Override
	public void run(double[] lambda) {
		Model m = createModel();

		Analysis an = new Analysis(m);

		Strategy s = createStrategy(m, lambda);

		s.setEchoLevel(EchoLevelType.OUTPUT, EchoLevelType.AUX1);

		an.setStrategy(s);

		addObservers(m, s, (TimeStepObserver) s.addObserver(new TimeStepObserver()));

		an.run();

		// postProcess(m);

		Debugger.warn("Analysis completed");
	}

}
